﻿using System.Threading;

namespace Core.RVO
{
    public class WorkerSystem : Component, IAwake
    {
        private Worker[] workers;
        private ManualResetEvent[] doneEvents;
        private int agentCount;

        public int Count { get; private set; }

        public float GlobalTime { get; private set; }
        public float TimeStep { get; private set; }

        public void Awake()
        {
            TimeStep = 0.25f;
        }

        public override void Dispose()
        {
            if (IsDisposed) return;
            base.Dispose();

        }

        public void SetNumWorkers(int num)
        {
            Count = num;

            if (Count <= 0)
            {
                ThreadPool.GetMinThreads(out int cnt, out _);
                Count = cnt;
            }
            foreach (Worker worker in workers)
            {
                worker.Dispose();
            }
            workers = null;
            agentCount = 0;

            workers = new Worker[Count];
            doneEvents = new ManualResetEvent[workers.Length];

            for (int i = 0; i < workers.Length; ++i)
            {
                doneEvents[i] = new ManualResetEvent(false);
                workers[i] = Entity.Create<Worker, ManualResetEvent>(doneEvents[i]);
            }
        }

        public void SetAgentCount(int count)
        {
            if (agentCount == count)
            {
                return;
            }

            agentCount = count;
            for (int i = 0; i < workers.Length; ++i)
            {
                workers[i].SetIndex(i * agentCount / workers.Length, (i + 1) * agentCount / workers.Length);
            }
        }

        public void Update()
        {
            for (int i = 0; i < workers.Length; ++i)
            {
                doneEvents[i].Reset();
                ThreadPool.QueueUserWorkItem(workers[i].Step);
            }

            WaitHandle.WaitAll(doneEvents);

            for (int i = 0; i < workers.Length; ++i)
            {
                doneEvents[i].Reset();
                ThreadPool.QueueUserWorkItem(workers[i].Update);
            }

            WaitHandle.WaitAll(doneEvents);

            GlobalTime += TimeStep;
        }
    }
}
